package com.xxx.base.util;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

public class Utils {

    public static final Object[] EMPTY_ARRAY = {};


//    public static void main(String[] args) {
//        System.out.println(httpGet(
//                "http://test.smaradio.com/api-v2-user/incar/getCollectedVodAlbums",
//                Collections.emptyMap(),
//                Collections.singletonMap("SessionId", "AC64ED19F70E4843A5A3601FF5310AE2"))
//        );
//    }


    public static String httpGet(String urlStr) {
        return httpGet(urlStr, 3000, Collections.emptyMap(), Collections.emptyMap());
    }


    public static String httpGet(String urlStr, Map<String, Object> params) {
        return httpGet(urlStr, 3000, params, Collections.emptyMap());
    }


    public static String httpGet(String urlStr, Map<String, Object> params, Map<String, Object> cookie) {
        return httpGet(urlStr, 3000, params, cookie);
    }


    public static String httpGet(String urlStr, int timeout, Map<String, Object> params, Map<String, Object> cookie) {
        String ret = null;
        try {
            URL url = new URL(fullUrl(urlStr, params));
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Accept-Charset", "UTF-8");

            if (isNotEmpty(cookie)) {
                StringBuilder sb = new StringBuilder();
                cookie.forEach((s, o) -> {
                    if (o != null) sb.append(s).append("=").append(o);
                });
                conn.setRequestProperty("Cookie", sb.toString());
            }

            if (timeout % 2 == 0) {
                conn.setConnectTimeout(timeout / 2);
                conn.setReadTimeout(timeout / 2);
            } else {
                conn.setConnectTimeout(timeout / 2);
                conn.setReadTimeout((timeout / 2) + 1);
            }
            conn.connect();
            ret = IOUtils.toString(conn.getInputStream(), "UTF-8");
        } catch (Exception e) {
            // ignore ...
            e.printStackTrace();
        }
        return ret;
    }


    public static String fullUrl(String urlStr, Map<String, Object> params) {
        if (isEmpty(params)) return urlStr;
        StringBuilder sb = new StringBuilder(urlStr).append("?");
        params.forEach((s, o) -> {
            if (o != null) sb.append(s).append("=").append(o).append("&");
        });
        return sb.toString();
    }


    public static String randomCronEl() {
        StringBuilder sb = new StringBuilder();
        Random r = new Random();
        sb.append(r.nextInt(59)).append(" ")
                .append(r.nextInt(59)).append(" ")
                .append(r.nextInt(6)).append(" ")
                .append("? * ");
        int i = r.nextInt(6) + 1;
        if (i == Calendar.SUNDAY) sb.append("SUN");
        else if (i == Calendar.MONDAY) sb.append("MON");
        else if (i == Calendar.TUESDAY) sb.append("TUE");
        else if (i == Calendar.WEDNESDAY) sb.append("WED");
        else if (i == Calendar.THURSDAY) sb.append("THU");
        else if (i == Calendar.FRIDAY) sb.append("FRI");
        else if (i == Calendar.SATURDAY) sb.append("SAT");
        return sb.toString();
    }


    public static void copy(Object pObj, Map<String, Object> pMap) {
        if (pObj == null || pMap == null) return;
        PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(pObj.getClass());
        for (PropertyDescriptor pd : pds) {
            try {
                Object value = pd.getReadMethod().invoke(pObj);
                pMap.put(pd.getName(), value);
            } catch (Exception e) {
                continue;
            }
        }
    }


    /**
     * 把一个bean 转换成 一个map
     * @param bean
     * @return
     */
    public static <T> ToMap toMapper(T bean) { return new ToMap(bean); }


    public static class ToMap<T> {
        private T                     bean;
        private Map<String, String>   propAlias;
        private Set<String>           ignore;
        private Map<String, Function> valueConverter;
        private boolean showClassProp;

        public ToMap(T bean) {
            this.bean = bean;
        }
        public ToMap<T> aliasProp(String originPropName, String aliasName) {
            if (propAlias == null) propAlias = new HashMap<>(7);
            propAlias.put(originPropName, aliasName);
            return this;
        }
        public ToMap<T> showClassProp() {
            showClassProp = true; return this;
        }
        public ToMap<T> ignore(String... propNames) {
            if (propNames == null) return this;
            if (ignore == null) ignore = new HashSet<>(7);
            for (String propName : propNames) ignore.add(propName);
            return this;
        }
        public ToMap<T> addConverter(String propName, Function converter) {
            if (valueConverter == null) valueConverter = new HashMap<>(7);
            valueConverter.put(propName, converter);
            return this;
        }
        public HashMap build() {
            HashMap map = new HashMap();
            if (bean == null) return map;
            PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(bean.getClass());
            for (PropertyDescriptor pd : pds) {
                try {
                    String propName = pd.getName();
                    if ((ignore != null && ignore.contains(propName)) || (!showClassProp && "class".equals(propName))) continue;
                    String aliasName = null;
                    if (propAlias != null && propAlias.containsKey(propName)) aliasName = propAlias.get(propName);
                    String resultName = (aliasName == null ? propName : aliasName);

                    Object value = pd.getReadMethod().invoke(bean);
                    if (valueConverter != null) {
                        if (valueConverter.containsKey(propName)) value = valueConverter.get(propName).apply(value);
                        else if (aliasName != null && valueConverter.containsKey(aliasName)) {
                            value = valueConverter.get(aliasName).apply(value);
                        }
                    }
                    map.put(resultName, value);
                } catch (Exception e) {
                    continue;
                }
            }
            return map;
        }
    }


    /**
     * 将一个长度不超过8的布尔数组转为一个字节
     *
     * @param bools 布尔数组
     * @return 如{true, false, false, true}将返回1001
     */
    public static Byte boolArrToByte(Boolean[] bools) {
        if (bools.length > 8) {
            throw new RuntimeException("布尔数组的长度超过一个Byte的容量");
        }
        Integer val = 0;
        List<Boolean> boolList = Arrays.asList(bools);
        for (int i = 0; i < boolList.size(); i++) {
            val = val * 2;
            val = val + (boolList.get(i) ? 1 : 0);
        }
        return new Byte(String.valueOf(val));
    }


    /**
     * 将List转为Map类型
     *
     * @param collection 要转换的collection
     * @param keyFunc    从collection中如何取得用在map的key的字段
     * @param valueFunc  从collection中如何取得用在map的value的字段
     * @param <T>        collection中元素的类型
     * @param <K>        map的key的类型
     * @param <V>        map的value的类型
     * @return 转换后的map
     */
    public static <T extends Object, K extends Object, V extends Object> Map<K, V> colToMap(Collection<T> collection, Function<T, K> keyFunc, Function<T, V> valueFunc) {
        Map<K, V> map = new HashMap<K, V>();
        if (collection == null) return map;
        collection.forEach(new Consumer<T>() {
            @Override
            public void accept(T t) {
                K key = keyFunc.apply(t);
                V value = valueFunc.apply(t);
                map.put(key, value);
            }
        });
        return map;
    }


    public static <T extends Object> String join(Collection<T> collection, Function<T, String> toStringFunc, String splitor) {
        Optional<String> optional = collection.stream().map(t -> toStringFunc.apply(t)).reduce((s, s2) -> s + splitor + s2);
        if (optional.isPresent())
            return optional.get();
        else
            return "";
    }


    public static <T extends Object> String join(Collection<T> collection, String splitor) {
        return join(collection, new Function<T, String>() {
            @Override
            public String apply(T t) {
                return t.toString();
            }
        }, splitor);
    }


    /**
     * 去掉一个url中多余的/
     * @param pURI
     * @return
     */
    public static String normalizePath(String pURI) {
        if (StringUtils.isBlank(pURI)) {
            return pURI;
        }
        String lPath = pURI;
        try {
            lPath = URI.create(pURI).normalize().toString();
        } catch (Exception e) {
        }
        return lPath;
    }


    public static int toInt(final Object obj, final int defaultValue) {
        if(obj == null) return defaultValue;
        if (obj instanceof Integer) return (Integer) obj;
        else if (obj instanceof String) {
            try {
                return Integer.parseInt(obj.toString());
            } catch (final NumberFormatException nfe) {
                return defaultValue;
            }
        }
        return defaultValue;
    }


    public static Integer toInteger(final Object obj, final Integer defaultValue) {
        if(obj == null) return defaultValue;
        if (obj instanceof Integer) return (Integer) obj;
        else if (obj instanceof String) {
            try {
                return Integer.parseInt(obj.toString());
            } catch (final NumberFormatException nfe) {
                return defaultValue;
            }
        }
        return defaultValue;
    }


    public static boolean isEmpty(Collection<?> pCollection) {
        return (pCollection == null || pCollection.isEmpty());
    }


    public static boolean isNotEmpty(Collection<?> pCollection) {
        return (!isEmpty(pCollection));
    }


    public static boolean isEmpty(Object[] array) {
        return (array == null || Array.getLength(array) == 0);
    }


    public static boolean isNotEmpty(Object[] array) {
        return !isEmpty(array);
    }


    public static boolean isEmpty(final Map<?, ?> map) {
        return map == null || map.isEmpty();
    }

    public static boolean isNotEmpty(final Map<?, ?> map) {
        return map != null && !map.isEmpty();
    }
}
